package com.sd365.permission.centre.service.impl;

import cn.hutool.db.handler.HandleHelper;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.sd365.common.core.annotation.mybatis.Pagination;
import com.sd365.common.core.annotation.stuffer.CommonFieldStuffer;
import com.sd365.common.core.annotation.stuffer.IdGenerator;
import com.sd365.common.core.annotation.stuffer.MethodTypeEnum;
import com.sd365.common.core.common.advice.MyPageInfo;
import com.sd365.common.core.common.api.CommonPage;
import com.sd365.common.core.common.exception.BusinessException;
import com.sd365.common.core.common.exception.code.BizErrorCode;
import com.sd365.common.core.common.service.AbstractBusinessService;
import com.sd365.common.core.context.BaseContextHolder;
import com.sd365.common.util.BeanUtil;
import com.sd365.permission.centre.dao.mapper.ResourceMapper;
import com.sd365.permission.centre.dao.mapper.RoleMapper;
import com.sd365.permission.centre.dao.mapper.RoleResourceMapper;
import com.sd365.permission.centre.entity.Resource;
import com.sd365.permission.centre.pojo.dto.ResourceDTO;
import com.sd365.permission.centre.pojo.dto.ResourceTreeDTO;
import com.sd365.permission.centre.pojo.query.ResourceQuery;
import com.sd365.permission.centre.pojo.vo.ResourceVO;
import com.sd365.permission.centre.pojo.vo.ResourceVO2;
import com.sd365.permission.centre.pojo.vo.RoleVO;
import com.sd365.permission.centre.service.ResourceService;
import com.sd365.permission.centre.service.cache.IResourceCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.javassist.util.HotSwapAgent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @version 1.0.1
 * @Class ResourceServiceImpl
 * @Description 资源管理服务类，主要实现资源的增删改查，为了提供认证和鉴权模块的执行效率该类
 * 提供了initRedis方法在容器启动的时候进行缓存初始化，该类使用spring cache注解维护缓存
 * @Author Administrator
 * @Date 2023-02-25  8:04
 */
@Slf4j
@Service
public class ResourceServiceImpl extends AbstractBusinessService implements ResourceService {
    /**
     * 缓存操作接口
     */
    @Autowired
    private IResourceCache resourceCache;
    /**
     * 资源mapper
     */
    @Autowired
    private ResourceMapper resourceMapper;
    /**
     * 基础框架中的id生成器
     */
    @Autowired
    private IdGenerator idGenerator;
    /**
     * redis操作模板
     */
    @javax.annotation.Resource
    private RedisTemplate redisTemplate;
    /**
     * 对应操作 basic_role_resource表
     */
    @Autowired
    private RoleResourceMapper roleResourceMapper;
    /**
     * 角色mappper
     */
    @Autowired
    private RoleMapper roleMapper;


    @Override
    public List<Resource> loadResource2Cache() {
//        List<Resource> resources = resourceMapper.commonQuery2();
//        log.debug("resources---------->{}", resources);
//        // 加载所有的resource覆盖内存的resource
//        for (Resource resource : resources) {
//            //    Global.RESOURCE_ID_KEY 后续使用 Common的 config模块
//            //redisTemplate.opsForValue().set(Global.RESOURCE_ID_KEY+resource.getId(),resource);
//            resourceCache.add2Cache(resource);
//        }
//        return resources;
        return new ArrayList<>();
    }

    @Override
    public ResourceDTO queryById(Long id) {
        Resource temp = resourceMapper.findById(id);
        return BeanUtil.copy(temp, ResourceDTO.class);
    }

    @Pagination
    @CommonFieldStuffer(methodType = MethodTypeEnum.QUERY)
    @Override
    public List<ResourceDTO> commonQuery(ResourceQuery resourceQuery) {
        List<Resource> resources = resourceMapper.commonQuery(resourceQuery.getRoleId());
        setPageInfo2BaseContextHolder((Page) resources);

        if (!CollectionUtils.isEmpty(resources)) {
            Page page = (Page) resources;
            BaseContextHolder.set("pageInfo", JSON.toJSONString(new MyPageInfo(page.getTotal(), page.getPages())));
            List<ResourceDTO> resourceDTOS = BeanUtil.copyList(resources, ResourceDTO.class);
            return resourceDTOS;
        } else {
            return new ArrayList<>();
        }
    }

    @Pagination
    @Override
    public List<ResourceDTO> query(ResourceQuery resourceQuery) {
        List<Resource> resources = resourceMapper.commonQuery(resourceQuery.getRoleId());
        Page page = (Page) resources;
        BaseContextHolder.set("pageInfo", JSON.toJSONString(new MyPageInfo(page.getTotal(), page.getPages())));
        return BeanUtil.copyList(resources, ResourceDTO.class);
    }


    @Override
    public List<ResourceVO2> query2(ResourceQuery resourceQuery) {
        List<ResourceVO2> temp = new ArrayList<>();
        //查找所有资源列表
        List<ResourceVO2> allNodes = resourceMapper.commonQuery2(resourceQuery.getRoleId());
        //找到所有父节点
        List<ResourceVO2> resourceVO2s = allNodes.stream().filter(item -> {
            if (item.getLeaf()==1) {
                return true;//是树的根节点
            } else {
                return false;
            }
        }).collect(Collectors.toList());
        //旧版父子关系
//        HashMap<ResourceVO2,List<ResourceVO2>> map  = new HashMap<>();
//        for(ResourceVO2 p  : temp2){
//            long father = p.getId();
//            for(ResourceVO2  q : temp2){
//                long son = q.getParentId();
//                if(father == son){
//                    List<ResourceVO2> list = map.getOrDefault(p,new ArrayList<>());
//                    list.add(q);
//                    map.put(p,list);
//                }
//            }
//        }
        //新版父子关系，时间复杂度O(N)
        HashMap<ResourceVO2,List<ResourceVO2>> map  = new HashMap<>();
        //存储所有id对应的对象
        HashMap<Long,ResourceVO2> idToRole = new HashMap<>();
        for(ResourceVO2 v1:allNodes){
            idToRole.put(v1.getId(),v1);
        }
        //遍历根结点
        for(ResourceVO2  son : allNodes){
           Long id = son.getParentId();
           //从idtorole得到对象
            ResourceVO2 father = idToRole.get(id);
            List<ResourceVO2> sons = map.getOrDefault(father, new ArrayList<>());
            sons.add(son);
            map.put(father,sons);
        }

        HashSet<Long> fatherId = new HashSet<>();
        for (ResourceVO2 father : resourceVO2s) {
            fatherId.add(father.getId());
            ResourceVO2 resourceVO2 = new ResourceVO2(father.getId(), father.getName());
            this.bfs(map,father,temp);
        }
        //去重，去掉所有子节点（只保留父节点里的children）
        List<ResourceVO2> res = new ArrayList<>();
        for(ResourceVO2 r : temp){
            if(fatherId.contains(r.getId())){
                res.add(r);
            }
        }
        return res;

    }
    private void bfs(HashMap<ResourceVO2,List<ResourceVO2>> map,ResourceVO2 father, List<ResourceVO2> fatherList) {
        Queue<ResourceVO2> queue = new LinkedList<>();
        queue.add(father);

        while (!queue.isEmpty()){
            ResourceVO2 top = queue.poll();

            fatherList.add(top);

            List<ResourceVO2> sons = map.getOrDefault(top,new ArrayList<>());
            top.setChildren(sons);
            for(ResourceVO2 son : sons){
                queue.add(son);
            }
        }
    }

    @Override
    public ResourceVO add(ResourceDTO resourceDTO) {
        // TODO 业务检查，检查在同一个子系统菜单不可以重复
        Resource resource = BeanUtil.copy(resourceDTO, Resource.class);
        //调用雪花计算ID
        if (ObjectUtils.isEmpty(resourceDTO.getId())) {
            resource.setId(idGenerator.snowflakeId());
        }

        ResourceVO resourceVO = resourceMapper.insert(resource) > 0
                ? BeanUtil.copy(resource, ResourceVO.class)
                : new ResourceVO();
        // 如果添加成功则同步到缓存
        if (resourceVO.getId() > 0) {
            resourceCache.add2Cache(resource);
        }
        return resourceVO;
    }

    @Override
    @CommonFieldStuffer(methodType = MethodTypeEnum.DELETE)
    public Boolean batchDelete(ResourceDTO[] resourceDTOS) {
        if (ObjectUtils.isEmpty(resourceDTOS)) {
            throw new BusinessException(BizErrorCode.PARAM_VALID_FIELD_REQUIRE, new Exception("batchDelete 方法错误"));
        }
        for (ResourceDTO resourceDTO : resourceDTOS) {
            Example example = new Example(Resource.class);
            example.createCriteria().andEqualTo("id", resourceDTO.getId());
            if (resourceMapper.deleteByExample(example) <= 0) {
                throw new BusinessException(BizErrorCode.DATA_DELETE_FAIL_EXITS_REF_RECORD, new Exception(String.format("删除的记录id %d 没有匹配到版本", resourceDTO.getId())));
            }
            // 同时删除缓存
            resourceCache.deleteCache(resourceDTO.getId());
        }
        return true;
    }

    @Override
    @CommonFieldStuffer(methodType = MethodTypeEnum.DELETE)
    public Boolean remove(Long id) {
        Example example = new Example(Resource.class);
        example.createCriteria().andEqualTo("id", id);
        boolean result = resourceMapper.deleteByExample(example) > 0;
        if (result) {
            // 手工方式维护cache
            resourceCache.deleteCache(id);
        }
        return result;

    }

    @Override
    public ResourceVO modify(ResourceDTO resourceDTO) {
        Resource resource = BeanUtil.copy(resourceDTO, Resource.class);
        if (resourceDTO.getVersion() != null) {
            Long version = resourceDTO.getVersion();
            version++;
            resource.setVersion(version);
        }
        Example example = new Example(Resource.class);
        example.createCriteria().andEqualTo("id", resourceDTO.getId());
        super.baseDataStuff4Updated(resource);
        ResourceVO resourceVO = resourceMapper.updateByExampleSelective(resource, example) > 0
                ? BeanUtil.copy(resource, ResourceVO.class)
                : new ResourceVO();
        if (resourceDTO.getId() > 0) {
            // 如果更新成功则同步缓存
            resourceCache.updateCache(resource);
        }
        return resourceVO;

    }

    @Override
    public ResourceDTO copy(Long id) {
        /**
         查询 id的客户信息
         修改 id 插入行的客户信息
         */
        Resource resource = resourceMapper.selectByPrimaryKey(id);
        if (resource != null) {
            resource.setId(idGenerator.snowflakeId());
            if (resourceMapper.insert(resource) > 0) {
                ResourceDTO resourceDTO = BeanUtil.copy(resource, ResourceDTO.class);
                return resourceDTO;
            } else {
                throw new BusinessException(BizErrorCode.DATA_INSERT_RETURN_EFFECT_LATTER_ZERO, new Exception("copy 方法错误"));
            }
        } else {
            throw new BusinessException(BizErrorCode.DATA_INSERT_RETURN_EFFECT_LATTER_ZERO, new Exception("INSERT 方法返回0"));
        }
    }


    @Override
    public ResourceDTO findByParentId(Long id) {
        return BeanUtil.copy(resourceMapper.findByParentId(id), ResourceDTO.class);
    }

    @Override
    public List<ResourceDTO> queryResourceByMenu() {
        Example example = new Example(Resource.class);
        example.createCriteria().andEqualTo("resourceType", (byte) 0);
        List<Resource> resources = resourceMapper.selectByExample(example);
        return BeanUtil.copyList(resources, ResourceDTO.class);

    }
}
